﻿#region Header

// Majesty of Omega - The next evolution of macro management space strategy
// 
// Copyright 2007-2009 André Claaßen
// 
// This file is protected by the terms and conditions of the
// Microsoft Community License (Ms-CL 1.1), a copy of which should
// have been distributed along with this software. If not,
// you may find the license information at the following URL:
// 
// http://www.microsoft.com/resources/sharedsource/licensingbasics/communitylicense.mspx

#endregion

#region

using System.Collections.Generic;
using System.Linq;
using Majesty_of_Omega.Model.Research;
using Majesty_of_Omega.Config;
#if NUNIT
using NUnit.Framework;
using TestClass = NUnit.Framework.TestFixtureAttribute;
using TestMethod = NUnit.Framework.TestAttribute;
using TestInitialize = NUnit.Framework.SetUpAttribute;
using TestCleanup = NUnit.Framework.TearDownAttribute;
using ClassInitialize = NUnit.Framework.TestFixtureSetUpAttribute;
using ClassCleanup = NUnit.Framework.TestFixtureTearDownAttribute;
#elif MBUNIT
using MbUnit.Framework;
using TestClass = MbUnit.Framework.TestFixtureAttribute;
using TestMethod = MbUnit.Framework.TestAttribute;
using TestInitialize = MbUnit.Framework.SetUpAttribute;
using TestCleanup = MbUnit.Framework.TearDownAttribute;
using ClassInitialize = MbUnit.Framework.TestFixtureSetUpAttribute;
using ClassCleanup = MbUnit.Framework.TestFixtureTearDownAttribute;
#else
using Microsoft.VisualStudio.TestTools.UnitTesting;
#endif

using Test.Majesty_of_Omega.TestUtils;

#endregion

namespace Test.Majesty_of_Omega.BL.Model
{
    /// <summary>
    /// Test class for the <see cref="ResearchManager" />
    /// </summary>
    [TestClass]
    public class TestResearchManager
    {
        /// <summary>
        /// Tests the constructor.
        /// </summary>
        [TestMethod]
        public void TestConstructor()
        {
            ResearchManager manager = new ResearchManager();
            Assert.AreEqual(manager.TechFieldResearchs.Count, SpreadSheetManager.TechTables.TechFields.Length);
            Assert.AreEqual(manager.TechSchoolResearchs.Count, SpreadSheetManager.TechTables.TechSchools.Length);
        }

        /// <summary>
        /// Tests the serialization of the <see cref="ResearchManager" />
        /// </summary>
        [TestMethod]
        public void TestSerialization()
        {
            var manager = new ResearchManager();
            manager.TechFieldResearchs[0].TechLevel = 25;
            var clone = (ResearchManager)SerializeHelper.SaveLoadAndCompare(manager, "Can't serialize the ResearchManager correctly");
            Assert.IsNotNull(clone.TechFieldResearchs[0].TechField);
            Assert.AreEqual(25, clone.TechFieldResearchs[0].TechLevel);
        }

        [TestMethod]
        public void TestBalanceResearchPointsForTechSchoolResearch()
        {
            ResearchManager researchManager = new ResearchManager();
            var researchs = researchManager.TechSchoolResearchs;
            var sumPercentage = researchs.Aggregate(0, (sum, tr) => sum + tr.ResearchPercentage);
            Assert.AreEqual(100, sumPercentage);
        }

        [TestMethod]
        public void TestBalanceResearcPointsForTechFieldResearch()
        {
            ResearchManager researchManager = new ResearchManager();
            var researchs = researchManager.GetTechFieldResearchForTechSchool(SpreadSheetManager.TechTables.TechSchools[0]);
            var sumPercentage = researchs.Aggregate(0, (sum, tr) => sum + tr.ResearchPercentage);
            Assert.AreEqual(100, sumPercentage);
        }

        /// <summary>
        /// This test creates an applied technology research project
        /// </summary>
        [TestMethod]
        public void TestCreateAppliedTechnologies()
        {
            const string expected = "LasrCann";

            ResearchManager researchManager = new ResearchManager();
            researchManager.TechCurrentAppliedResearchProjects.Add(new AppliedResearchItem(expected));
            Assert.AreEqual(1, researchManager.TechCurrentAppliedResearchProjects.Count);
            AppliedResearchItem item = researchManager.TechCurrentAppliedResearchProjects[0];
            Assert.AreEqual(expected, item.ResearchItemTypeID);
            Assert.AreEqual(FundingLevel.Full, item.BudgetFundingLevel);
            Assert.AreEqual(100, item.ResearchPercentage, "The default research speed is 100 percent");
        }

        /// <summary>
        /// Tests the "Add available research item to current applied research projects" function
        /// </summary>
        /// <see cref="ResearchManager.AddResearchToCurrentAppliedResearchProjects"/>
        [TestMethod]
        public void TestAddResearchToCurrentAppliedResearchProjects()
        {
            ResearchManager researchManager = new ResearchManager();
            var appliedResearchItem = new AppliedResearchItem("LasrCann");
            researchManager.TechAvailableAppliedResearchs.Add(appliedResearchItem);
            Assert.AreEqual(1, researchManager.TechAvailableAppliedResearchs.Count);
            researchManager.AddResearchToCurrentAppliedResearchProjects(appliedResearchItem);
            Assert.AreEqual(0, researchManager.TechAvailableAppliedResearchs.Count);
            Assert.AreEqual(1, researchManager.TechCurrentAppliedResearchProjects.Count);
        }

        /// <summary>
        /// Tests the research points summarize functions of the ResearchManager.
        /// </summary>
        [TestMethod, Ignore]
        public void TestResearchPointsSummarizeFunctions()
        {
            ResearchManager researchManager = new ResearchManager {TotalResearchPointsOfCurrentTurn = 200};
            AppliedResearchItem researchItem = new AppliedResearchItem("LasrCann");
            researchManager.TechCurrentAppliedResearchProjects.Add(researchItem);
            Assert.AreEqual(50, researchManager.SumOfAppliedResearchPoints);
            Assert.AreEqual(150, researchManager.SumOfFundamentalResearchPoints);
        }

        /// <summary>
        /// Tests the upcoming technology property in a special case.
        /// </summary>
        [TestMethod]
        public void TestUpcomingTechnology1()
        {
            // Create a researchmanager with only one tech field (AstroPhysics on tech level 27)
            ResearchManager manager = new ResearchManager
                                          {
                                              TechFieldResearchs =
                                                  new List<TechFieldResearch>
                                                      {
                                                          new TechFieldResearch(null, SpreadSheetManager.TechTables.TechFieldDictionary["AstPh"]){TechLevel = 27}
                                                      }
                                          };

            var nextTechnologies = manager.GetNextUpcomingTechnologies(4);
            Assert.AreEqual(1, nextTechnologies.Count());
            Assert.AreEqual(SpreadSheetManager.TechTables.TechAppDictionary["GeoHarmP"], nextTechnologies.First());
        }

        /// <summary>
        /// Tests the upcoming technology property in a special case.
        /// </summary>
        [TestMethod]
        public void TestUpcomingTechnologyStandard()
        {
            // Create a researchmanager with only one tech field (AstroPhysics on tech level 27)
            ResearchManager manager = new ResearchManager();
            const int nextLevels = 5;
            var nextTechnologies = manager.GetNextUpcomingTechnologies(nextLevels);
            Assert.IsTrue(nextTechnologies.All(ta => ta.TechFieldDependencies.Max(tfd => tfd.TechLevel) <= nextLevels),
                          "asserts, that all upcoming technologies have a techlevel with max = " + nextLevels);
        }

        /// <summary>
        /// Tests the <see cref="ResearchManager.UnlockTechField" /> function
        /// </summary>
        [Test]
        public void TestUnlockTechField()
        {
            ResearchManager manager = new ResearchManager();
            AssertTechFieldsResearched(4, "PS", manager, "Without unlock, there are 4 tech fields");
            manager.UnlockTechField(manager.GetTechFieldResearch("AstEn"));
            AssertTechFieldsResearched(5, "PS", manager, "With unlock, we have 5 tech fields");
        }

        /// <summary>
        /// Asserts the tech fields researched for the given tech schol
        /// </summary>
        /// <param name="numOfTechFields">The num of tech fields.</param>
        /// <param name="techSchoolType">Type of the tech school.</param>
        /// <param name="manager">The manager.</param>
        /// <param name="message">The message.</param>
        private static void AssertTechFieldsResearched(int numOfTechFields, string techSchoolType, ResearchManager manager, string message)
        {
            var techFields =
                manager.GetTechFieldResearchForTechSchool(SpreadSheetManager.TechTables.TechSchoolDictionary[techSchoolType]);
            int sumPercentage = techFields.Sum(tf => tf.ResearchPercentage);
            Assert.AreEqual(numOfTechFields, techFields.Count(), message);
            Assert.AreEqual(100, sumPercentage);
            Assert.IsTrue(techFields.All(tf => tf.ResearchPercentage > 0),
                          "All tech fields must have a research greater than 0");
        }

        /// <summary>
        /// Tests the <see cref="TechSchoolResearch.TechLevel" /> function
        /// </summary>
        [Test]
        public void TestCalcTechLevelForTechSchool()
        {
            ResearchManager manager = new ResearchManager();
            var techSchoolResearchEB  = manager.TechSchoolResearchs.Find(tr => tr.TechSchool.Type_ID == "EB");
            int level = techSchoolResearchEB.TechLevel;
            Assert.AreEqual(0, level);
            Assert.AreEqual(2, techSchoolResearchEB.GetResearchedTechFields().Count() );
            
            // raise tech level for the first tech field of the school to 4
            techSchoolResearchEB.GetResearchedTechFields().First().TechLevel = 4;
            
            // now we must have an average tech level for the tech school == 2
            Assert.AreEqual(2, techSchoolResearchEB.TechLevel);
        }

        /// <summary>
        /// Tests the <see cref="TechFieldResearch.Icon" /> property
        /// </summary>
        [Test]
        public void TestTechFieldIconProperty()
        {
            ResearchManager manager = new ResearchManager();
            const string techFieldTypeID = "Econo";
            var techFieldResearch = manager.GetTechFieldResearch(techFieldTypeID);
            Assert.AreEqual(SpreadSheetManager.TechTables.TechFieldDictionary[techFieldTypeID].IconName,
                            techFieldResearch.Icon);
        }

        /// <summary>
        /// Tests the <see cref="AppliedResearchItem.TurnCost" /> property
        /// </summary>
        [Test]
        public void TestTurnCost()
        {
            // HydroFrm is a tech level 2 technology
            AppliedResearchItem item = new AppliedResearchItem(TestConstants.HydroFarmTechId);
            int turnCost = item.TurnCost;
            Assert.AreEqual(40, turnCost);

            // Calculation:
            // ....................................................................................
            // 40 = (ResearchPointsPerField(Level 2) - ResearchPointsPerField(Level 1)) * 0,3
            // 40 = (                 232            -                       100      ) * 0,3
            // 40 =                            Round(     132 * 0,3 )
            // 40 =  40
        }

        /// <summary>
        /// Tests the <see cref="ResearchManager.StartAppliedResearchProject" /> property
        /// </summary>
        [TestMethod]
        public void TestStartAppliedResearchProject()
        {
            ResearchManager manager = new ResearchManager();
            AppliedResearchItem item = new AppliedResearchItem(TestConstants.HydroFarmTechId);
            manager.TechAvailableAppliedResearchs.Add(item);
            manager.StartAppliedResearchProject(item, FundingLevel.Full);
            ResearchConfigurationResearchDefaults researchDefaults = SpreadSheetManager.ResearchConfiguration.ResearchDefaults;
            Assert.IsTrue(
                item.PercentageResearchPerTurn >= researchDefaults.PercentagePerTurnMin &&
                item.PercentageResearchPerTurn <= researchDefaults.PercentagePerTurnMax
            );
            Assert.IsTrue(item.TurnsRemaining > 1);
        }

        /// <summary>
        /// Tests the <see cref="ResearchManager.ApplyFullyResearchedTechnology" /> function
        /// </summary>
        [Test]
        public void TestApplyFullyResearchedTechnology()
        {
            ResearchManager manager = new ResearchManager();
            AppliedResearchItem item  = new AppliedResearchItem(TestConstants.HydroFarmTechId);
            int researchedTechsCount= manager.ResearchedTechApplications.Count;
            manager.TechCurrentAppliedResearchProjects.Add(item);

            // apply 100 percent research
            item.PercentageResearchPerTurn = 100;
            item.AddResearchForTurn();

            Assert.IsTrue(item.IsFullyResearched);

            manager.ApplyFullyResearchedTechnology(item);
            Assert.AreEqual(1 + researchedTechsCount, manager.ResearchedTechApplications.Count);
        }

        /// <summary>
        /// Tests the function <see cref="ResearchManager.GetTechApplicationStateForFieldAndLevel"/>
        /// </summary>
        /// <remarks>
        /// When I search the tech applications for the tech field bioharvesting
        /// and level 2, I must got Hydro Farm with unknown research state
        /// </remarks>
        [Test]
        public void TestTechApplicationStateInfos()
        {
            const int techLevel2 = 2;
            
            // Testing Hydrop Farm: Bioharvest Tech 2
            ResearchManager manager = new ResearchManager();

            List<TechApplicationStateInfo> stateInfos = manager.GetTechApplicationStateForFieldAndLevel(
                SpreadSheetManager.TechTables.TechFieldDictionary[TestConstants.BioharvestTechFieldId],
                techLevel2, ResearchManager.TechApplicationState.GetAllTechApps);

            Assert.AreEqual(1, stateInfos.Count, "There must be one tech application");
            TechApplicationStateInfo bioharvestTechStateInfo = stateInfos.First();
            Assert.AreEqual(TestConstants.HydroFarmTechId, bioharvestTechStateInfo.TechApplication.Type_ID);
            Assert.AreEqual(ResearchState.Unknown, bioharvestTechStateInfo.ResearchState);
        }
    }
}